package org.jeecg.modules.scm.service.impl;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.modules.flow.api.FlowBaseCloudApi;
import org.jeecg.modules.flow.query.StartInstanceQuery;
import org.jeecg.modules.flow.query.TaskQuery;
import org.jeecg.modules.flow.vo.TaskVo;
import org.jeecg.modules.scm.constant.ScmConstant;
import org.jeecg.modules.scm.dto.EditReceivablesDto;
import org.jeecg.modules.scm.entity.ScmReceivables;
import org.jeecg.modules.scm.entity.ScmReceivablesInvoice;
import org.jeecg.modules.scm.mapper.ScmReceivablesMapper;
import org.jeecg.modules.scm.service.IScmInvoiceService;
import org.jeecg.modules.scm.service.IScmReceivablesInvoiceService;
import org.jeecg.modules.scm.service.IScmReceivablesService;
import org.jeecg.modules.scm.vo.InvoiceRelevancyVo;
import org.jeecg.modules.scm.vo.ReceivablesTaskVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * @Description: scm_receivables
 * @Author: jeecg-boot
 * @Date: 2021-10-25
 * @Version: V1.0
 */
@Service
@Slf4j
public class ScmReceivablesServiceImpl extends ServiceImpl<ScmReceivablesMapper, ScmReceivables> implements IScmReceivablesService {

    @Autowired
    private IScmReceivablesInvoiceService receivablesInvoiceService;

    @Autowired
    private FlowBaseCloudApi flowBaseCloudApi;

    @Autowired
    private IScmInvoiceService scmInvoiceService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public boolean updateByDto(EditReceivablesDto dto) {
        ScmReceivables old = baseMapper.selectById(dto.getId());
        if (old.getStatus() > ScmConstant.RECEIVABLES_STATUS_UN_CONFIRMED) {
            throw new JeecgBootException("当前状态不允许修改");
        }
        ScmReceivables receivables = new ScmReceivables();
        BeanUtils.copyProperties(dto, receivables);
        return baseMapper.updateById(receivables) > 0;
    }

    @Override
    public Integer saveByDto(EditReceivablesDto dto) {
        ScmReceivables receivables = new ScmReceivables();
        BeanUtils.copyProperties(dto, receivables, "id");
        baseMapper.insert(receivables);
        return receivables.getId();
    }

    @Override
    public String uploadContract(MultipartFile file, String no, Integer id) {
        ScmReceivables receivables = baseMapper.selectById(id);
        if (receivables == null) {
            throw new JeecgBootException("应收账款不存在");
        }

        String url = CommonUtils.upload(file, "/receivables/contract", CommonConstant.UPLOAD_TYPE_MINIO);
        String path = URLUtil.getPath(url);
        // TODO 需要对合同附件和合同编号的变化做出进一步的处理
        boolean isOk = lambdaUpdate().eq(ScmReceivables::getId, id).set(ScmReceivables::getContractAccessory, path)
                .set(StringUtils.isBlank(receivables.getContractNo()), ScmReceivables::getContractNo, no).update();

        return isOk ? path : "";
    }

    @Override
    public String uploadAccessory(MultipartFile file, Integer id) {
        ScmReceivables receivables;
        Optional<ScmReceivables> optional = lambdaQuery().select(ScmReceivables::getId, ScmReceivables::getOtherMaterials)
                .eq(ScmReceivables::getId, id).oneOpt();
        if (!optional.isPresent()) {
            throw new JeecgBootException("应收账款不存在");
        }
        receivables = optional.get();
        String paths = receivables.getOtherMaterials();
        String url = CommonUtils.upload(file, "/receivables/materials", CommonConstant.UPLOAD_TYPE_MINIO);
        String path = URLUtil.getPath(url);
        if (StringUtils.isBlank(paths)) {
            paths = path;
        } else {
            paths = paths + "," + path;
        }
        boolean isOk = lambdaUpdate().eq(ScmReceivables::getId, id)
                .set(ScmReceivables::getUpdateTime, new Date())
                .set(ScmReceivables::getOtherMaterials, paths).update();
        return paths;
    }

    @Override
    public Result<InvoiceRelevancyVo> relevancyInvoice(Integer id, List<String> invoiceNos) {
        InvoiceRelevancyVo resVo = new InvoiceRelevancyVo();
        List<Map<String, String>> errMsg = new ArrayList<>();
        resVo.setFailedCount(0);
        resVo.setSuccessfulCount(0);
        resVo.setFailedMsg(errMsg);
        // 检查应收账款记录是否存在
        if (!existsById(id)) {
            return Result.error("应收账款信息不存在", resVo);
        }

        for (String invoiceNo : invoiceNos) {
            Map<String, String> tempMsg = new HashMap<>(1);
            ScmReceivablesInvoice ri = new ScmReceivablesInvoice();
            ri.setReceivablesId(id);
            ri.setInvoiceNo(invoiceNo);

            // 插入关联表并更新发票状态
            if (!relevancyAndUpdateInvoiceState(ri, tempMsg)) {
                errMsg.add(tempMsg);
                resVo.setFailedCount(resVo.getFailedCount() + 1);
                continue;
            }
            resVo.setSuccessfulCount(resVo.getSuccessfulCount() + 1);
        }
        return Result.OK(resVo);
    }

    /**
     * 插入关联表、更新发票状态
     *
     * @param ri
     * @return
     */
    public Boolean relevancyAndUpdateInvoiceState(ScmReceivablesInvoice ri, Map<String, String> msgMap) {
        Boolean execute = transactionTemplate.execute((status) -> {
            try {
                if (!receivablesInvoiceService.save(ri)) {
                    msgMap.put(ri.getInvoiceNo(), "关联失败，请刷新重试!");
                    return Boolean.FALSE;
                }
                if (!scmInvoiceService.updateStateByNo(ri.getInvoiceNo())) {
                    msgMap.put(ri.getInvoiceNo(), "发票已关联应收账款，或发票不存在,请刷新重试");
                    status.setRollbackOnly();
                    return Boolean.FALSE;
                }
            } catch (Exception e) {
                String msg = e instanceof DuplicateKeyException ? "应收账款已关联该发票" : "关联失败，请刷新重试!";
                msgMap.put(ri.getInvoiceNo(), msg);
                log.error(msg, e);
                status.setRollbackOnly();
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        });

        return execute;
    }

    @Override
    public void disassociation(Integer id, List<String> invoiceNos) {

        for (String invoiceNo : invoiceNos) {
            receivablesInvoiceService.lambdaUpdate().eq(ScmReceivablesInvoice::getReceivablesId, id)
                    .eq(ScmReceivablesInvoice::getInvoiceNo, invoiceNo).remove();
        }
    }

    @Override
    public String applyCloudChain(Integer id) {
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        log.info(user.toString());
        if (user.getOrganization() == null) {
            throw new JeecgBootException("当前用户机构为空!");
        }
        String orgId = user.getOrganization().getOrganizationId();
        ScmReceivables receivables = baseMapper.selectById(id);
        if (receivables == null) {
            throw new JeecgBootException("应收账款不存在");
        }
        // 计算到期时间差
        long betweenDay = DateUtil.date().getTime() - receivables.getDueTime().getTime();
        if (betweenDay > 0) {
            throw new JeecgBootException("应收账款已到期");
        }
        // 验证应收账款的状态
        if (receivables.getStatus() > ScmConstant.RECEIVABLES_STATUS_CONFIRMED) {
            throw new JeecgBootException("应收账款当前状态不能申请");
        }
        // TODO 其他效验

        Map<String, Object> var = new HashMap<>();

        StartInstanceQuery query = new StartInstanceQuery();

        // 名称加上当前时间
        query.setName(user.getRealname() + "在+" + DateUtil.date() + "发起的:应收账款申请云链单申请");
        // 核心企业,这里传入的是应收账款对应的债务人客户号
        query.setCusNo(receivables.getOriginalDebtorCusNo());
        query.setOrgId(orgId);
        // 设置业务种类
        query.setBusinessKey("apply-cloud-chain:" + receivables.getId().toString());
        // 设置流程变量
        query.setVariables(var);
        // 调用流程服务，启动流程实例
        Result<String> result = flowBaseCloudApi.startInstanceByProKey(query);
        if (result == null || !result.isSuccess() || StringUtils.isBlank(result.getResult())) {
            throw new JeecgBootException("启动流程实例异常，" + (result == null ? "" : result.getMessage()));
        }
        // 更新应收账款的状态为--在流程中
        lambdaUpdate().eq(ScmReceivables::getId, receivables.getId())
                .set(ScmReceivables::getStatus, ScmConstant.RECEIVABLES_STATUS_IN_FLOW)
                .update();

        // 返回启动后的流程实例
        return result.getResult();
    }

    @Override
    public List<ReceivablesTaskVo> findTaskList(TaskQuery query) {

        Result<Page<TaskVo>> result = flowBaseCloudApi.findTasks(query, 0, Integer.MAX_VALUE);
        if (!result.isSuccess()) {
            throw new JeecgBootException("查询任务失败!");
        }
        if (CollectionUtils.isEmpty(result.getResult().getRecords())) {
            return null;
        }

        List<TaskVo> taskVos = result.getResult().getRecords();
        List<ReceivablesTaskVo> receivablesTaskVos = new ArrayList<>(taskVos.size());
        for (TaskVo taskVo : taskVos) {
            ReceivablesTaskVo temp = new ReceivablesTaskVo();
            BeanUtils.copyProperties(taskVo, temp);
            receivablesTaskVos.add(temp);
        }
        return receivablesTaskVos;
    }

    @Override
    public boolean updateStatusById(Integer status, Integer receivablesId) {
        return lambdaUpdate().eq(ScmReceivables::getId, receivablesId).set(ScmReceivables::getStatus, status).update();
    }

    @Override
    public Boolean deleteContract(Integer id) {
        if (id == null || id <= 0) {
            throw new JeecgBootException("应收账款id不能为空");
        }
        boolean updated = lambdaUpdate().eq(ScmReceivables::getId, id).set(ScmReceivables::getContractAccessory, null)
                .set(ScmReceivables::getContractNo, null).update();
        return updated;
    }

    @Override
    public Boolean existsById(Integer id) {
        return lambdaQuery().eq(ScmReceivables::getId, id).exists();
    }
}
